﻿using System;
using System.Collections.Generic;
using System.Text;
using FlexLib.FileTree;
using System.Reflection;
using FlexminAPI;
using FlexLib.RegistryTree;
using System.Drawing;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
using FlexLib.Plugins;

namespace FlexLib.Plugins
{
	public class InstPackage
	{
		public InstPackage(PluginInfo info)
		{
			moduleName = info.Name;
			displayName = info.DisplayName;
			version = info.PluginVersion;
			description = info.Description;
			isMultihost = info.Multihost;
			isResident = info.Resident;
		}
 
		public InstPackage() { }

		private string icon;
		public string Icon
		{
			get { return icon; }
			set { icon = value; }
		}

		private string moduleName;
		public string ModuleName
		{
			get { return moduleName; }
			set { moduleName = value; }
		}

		private string displayName;
		public string DisplayName
		{
			get { return displayName; }
			set { displayName = value; }
		}

		private Version version;
		public Version Version
		{
			get { return version; }
		}

		private string description;
		public string Description
		{
			get { return description; }
			set { description = value; }
		}

		private bool isMultihost;
		public bool IsMultihost
		{
			get { return isMultihost; }
			set { isMultihost = value; }
		}

		private bool isResident;
		public bool IsResident
		{
			get { return isResident; }
			set { isResident = value; }
		}

		private FileTreeNode rootNodeAdmin = new FileTreeNode("File System");
		public FileTreeNode RootNodeAdmin
		{
			get { return rootNodeAdmin; }
			set { rootNodeAdmin = value; }
		}

		private FileTreeNode rootNodeUser = new FileTreeNode("File System");
		public FileTreeNode RootNodeUser
		{
			get { return rootNodeUser; }
			set { rootNodeUser = value; }
		}

		private RegTreeKey rootKeyUser = new RegTreeKey("Registry");
		public RegTreeKey RootKeyUser
		{
			get { return rootKeyUser; }
			set { rootKeyUser = value; }
		}

		private RegTreeKey rootKeyAdmin = new RegTreeKey("Registry");
		public RegTreeKey RootKeyAdmin
		{
			get { return rootKeyAdmin; }
			set { rootKeyAdmin = value; }
		}

		private string adminDllPath;
		public string AdminDllPath
		{
			get { return adminDllPath; }
			set
			{ 
				adminDllPath = value;
				try
				{
					AssemblyName assemblyName = AssemblyName.GetAssemblyName(this.AdminDllFiles["File system\\" + value]);
					version = assemblyName.Version;
				}
				catch { }
			}
		}

		private string userDllPath;
		public string UserDllPath
		{
			get { return userDllPath; }
			set
			{
				userDllPath = value;
				try
				{
					AssemblyName assemblyName = AssemblyName.GetAssemblyName(this.UserDllFiles[value]);
					this.version = assemblyName.Version;
					this.moduleName = assemblyName.Name;
				}
				catch { }
			}
		}

		public Dictionary<string, string> AdminDllFiles = new Dictionary<string, string>();
		public Dictionary<string, string> UserDllFiles = new Dictionary<string, string>();

		public PluginInfo ToPluginInfo()
		{
			return new PluginInfo(moduleName, displayName, description, version, isResident, isMultihost);
		}

		private void ZipFiles(FileTreeNode node, ZipOutputStream zStream, string path, LogProgressHandler handler)
		{
			ZipEntry entry;
			FileStream fs;
			byte[] buf = new byte[4096];
			int percent = 0;

			foreach (FileTreeNode subNode in node.SubNodes)
			{
				string sub_path = path + '\\' + subNode.Name;
				entry = new ZipEntry(sub_path);
				entry.ExternalFileAttributes = 16; // Magic
				entry.DateTime = DateTime.Now;
				zStream.PutNextEntry(entry);

				if (subNode.Files.Count > 0)
					foreach (string file in subNode.Files)
					{
						if (File.Exists(file))
						{
							handler(percent, "Writing " + file);
							entry = new ZipEntry(sub_path + '\\' + Path.GetFileName(file));
							entry.DateTime = DateTime.Now;
							zStream.PutNextEntry(entry);
							fs = File.OpenRead(file);
							int source;
							do
							{
								source = fs.Read(buf, 0, buf.Length);
								zStream.Write(buf, 0, source);
							}
							while (source > 0);
							fs.Close();
							elementsDone++;
							percent = (int)((double)elementsDone / (double)elementsCount * 100.0);
							handler(percent, file  + " written");
						}
					}				
				ZipFiles(subNode, zStream, sub_path, handler);
			}
		}

		public delegate void LogProgressHandler(int percent, string message);

		private int GetFilesCount(FileTreeNode node)
		{
			int ret = node.Files.Count;
			foreach (FileTreeNode subNode in node.SubNodes)
				ret += GetFilesCount(subNode);
			return ret;	
		}

		private int CountElements()
		{
			int percent = 0;
			percent += GetFilesCount(RootNodeAdmin);
			percent += GetFilesCount(RootNodeUser);
			percent++;
			if (this.Icon != null)
				percent++;
			return percent;
		}

		private int elementsDone;
		private int elementsCount;

		public void Build(object handlerObject)
		{
			LogProgressHandler handler = handlerObject as LogProgressHandler;

			handler(0, "Package build start");
			handler(0, "===================");
			handler(0, "Building project: " + this.ModuleName);
			handler(0, "Admin DLL: " + this.AdminDllPath);
			handler(0, "User DLL: " + this.UserDllPath);
			handler(0, "Version: " + this.Version + '\n');			

			elementsCount = CountElements();
			elementsDone = 0;
			int percent = 0;

			ZipOutputStream zStream = new ZipOutputStream(File.Create(this.ModuleName + ".fpkg"));
			zStream.SetLevel(5);

			ZipFiles(this.RootNodeAdmin, zStream, "Admin", handler);
			ZipFiles(this.RootNodeUser, zStream, "User", handler);

			handler(percent, "Starting writing file module.xml");
			ZipEntry entry = new ZipEntry("module.xml");
			entry.DateTime = DateTime.Now;
			zStream.PutNextEntry(entry);
			XmlPluginSerialization.GenerateModuleInfoXML(zStream, this.ToPluginInfo(), this.AdminDllPath, this.UserDllPath, null);
			elementsDone++;
			percent = (int)((double)elementsDone / (double)elementsCount * 100.0);
			handler(percent, "File: module.xml written");
			File.Delete("module.xml");

			if (this.Icon != null && this.Icon != "")
			{
				handler(percent, "Writing icon");
				entry = new ZipEntry(Path.GetFileName(this.Icon));
				entry.DateTime = DateTime.Now;
				zStream.PutNextEntry(entry);
				FileStream fs = File.OpenRead(this.Icon);
				byte[] buf = new byte[4096];
				int source;
				do
				{
					source = fs.Read(buf, 0, buf.Length);
					zStream.Write(buf, 0, source);
				}
				while (source > 0);
				fs.Close();
				elementsDone++;
				percent = (int)((double)elementsDone / (double)elementsCount * 100.0);
				handler(percent, "Module icon written");
			}

			zStream.Finish();
			zStream.Close();
			handler(99, "Package " + Directory.GetCurrentDirectory() + "\\" + this.ModuleName + ".fpkg built successfully!");
			handler(100, "Done");
		}
	}
}
